//
// Created by vivi on 17/02/2018.
//
#include "core/engine/strandbasedhe.h"
#include "core/util/stringutil.h"

namespace HairEngine {

	StrandBasedHairEngine::StrandCountIterator::StrandCountIterator(const StrandBasedHairEngine *hairEnginePtr_,
	                                                                size_t strandIndex_):
			hairEnginePtr(hairEnginePtr_),
			strandIndex(strandIndex_) {
	}

	bool StrandBasedHairEngine::StrandCountIterator::isValidIterator() const {
		return (strandIndex < hairEnginePtr->getStrandCount());
	};

	bool StrandBasedHairEngine::StrandCountIterator::operator==(const StrandCountIterator &other) const {
		return (strandIndex == other.strandIndex) && (hairEnginePtr == other.hairEnginePtr);
	}

	bool StrandBasedHairEngine::StrandCountIterator::operator!=(const StrandCountIterator &other) const {
		return !(*this == other);
	}

	StrandBasedHairEngine::StrandCountIterator StrandBasedHairEngine::StrandCountIterator::operator++(int) {
		StrandCountIterator unchanged = *this;
		++this->strandIndex;
		return unchanged;
	}

	StrandBasedHairEngine::StrandCountIterator& StrandBasedHairEngine::StrandCountIterator::operator++() {
		++this->strandIndex;
		return *this;
	}

	size_t StrandBasedHairEngine::StrandCountIterator::operator*() const {
		Assert(isValidIterator());
		return hairEnginePtr->getParticleCountOnStrand(strandIndex);
	}

	StrandBasedHairEngine::ParticleIterator::ParticleIterator(
			const StrandBasedHairEngine *hairEnginePtr_, size_t strandIndex_, size_t particleLocalIndex_):
			hairEnginePtr(hairEnginePtr_),
			strandIndex(strandIndex_),
			particleLocalIndex(particleLocalIndex_)
	{
	}

	bool StrandBasedHairEngine::ParticleIterator::isValidIterator() const {
		return (strandIndex < hairEnginePtr->getStrandCount())
			&& (particleLocalIndex < hairEnginePtr->getParticleCountOnStrand(strandIndex));
	}

	bool StrandBasedHairEngine::ParticleIterator::operator==(const ParticleIterator &other) const {
		/*
		 * We place the pointer checking in the last for computation speed. For the general
		 * use case is that to check with the same hairEnginePtr
		 */
		return (strandIndex == other.strandIndex)
               && (particleLocalIndex == other.particleLocalIndex)
				&& (hairEnginePtr == other.hairEnginePtr);
	}

	bool StrandBasedHairEngine::ParticleIterator::operator!=(const ParticleIterator &other) const {
		return !(this->operator==(other));
	}

	//Prefix
	StrandBasedHairEngine::ParticleIterator& StrandBasedHairEngine::ParticleIterator::operator++() {
		++particleLocalIndex;
		if (particleLocalIndex >= hairEnginePtr->getParticleCountOnStrand(strandIndex)) {
			++strandIndex;
			particleLocalIndex = 0;
		}
		return *this;
	}

	StrandBasedHairEngine::ParticleIterator StrandBasedHairEngine::ParticleIterator::operator++(int) {
		auto unchanged = *this;
		++(*this);
		return unchanged;
	}

	const Eigen::Vector3f& StrandBasedHairEngine::ParticleIterator::operator*() const {
		Assert(isValidIterator());
		return hairEnginePtr->getParticlePosition(strandIndex, particleLocalIndex);
	}

	StrandBasedHairEngine::ParticleIterator StrandBasedHairEngine::getBeginParticleIterator() const {
		return {this, 0, 0};
	}

	StrandBasedHairEngine::ParticleIterator StrandBasedHairEngine::getEndParticleIterator() const {
		return {this, getStrandCount(), 0};
	};

	StrandBasedHairEngine::StrandCountIterator StrandBasedHairEngine::getBeginStrandCountIterator() const {
		return {this, 0};
	}

	StrandBasedHairEngine::StrandCountIterator StrandBasedHairEngine::getEndStrandCountIterator() const {
		return {this, getStrandCount()};
	}

	//Not thread safe
	void StrandBasedHairEngine::writeToFile(const std::string & filePath) const {

		//If we haven't created the StrandBasedHairGeometry
		if (hairGeometryPtr == nullptr) {
			hairGeometryPtr = std::make_unique<StrandBasedHairGeometry>(getBeginParticleIterator(), getEndParticleIterator(),
			                                   getBeginStrandCountIterator(), getEndStrandCountIterator());
		} else {
			hairGeometryPtr->assignParticlePositions(getBeginParticleIterator(), getEndParticleIterator());
		}

		hairGeometryPtr->writeToFile(filePath);
	}

	void StrandBasedHairEngine::writeToDirectory(const std::string &directoryPath, const std::string &fileName) const {
		std::string pathSeparator = StringUtility::getPathSeparator();
		std::string filePath = directoryPath
		                       + (StringUtility::endswith(directoryPath, pathSeparator) ? "" : pathSeparator)
		                       + fileName;
		writeToFile(fileName);
	}
}